#include "ShadowScence.h"

#include "Camera.h"
#include "Debuger.h"
#include "RanderTarget.h"
#include "Render.h"
#include "MaterialSrc/DrawShadowDepthMaterial.h"
#include "DefaultMeshFactory.h"
#include "Mesh.h"
#include "MaterialSrc/CubeMaterial.h"
#include "MaterialSrc/DrawImgMaterial.h"
#include "MaterialSrc/ShadowLightMaterial.h"

void FShadowScence::Init(FRender* InRender)
{
	FScence::Init(InRender);

	Camera = CreateDefaultCamera();

	constexpr glm::vec3 LightPos(-2.0f, 4.0f, -1.0f);

	constexpr float NearPlane = 1.0f;
	constexpr float FarPlane = 7.5f;

	const glm::mat4 LightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, NearPlane, FarPlane);
	const auto LightView = glm::lookAt(LightPos, glm::vec3(0.0f), glm::vec3(0.0, 1.0, 0.0));
	LightPvMat = LightProjection * LightView;

	// Shadow Depth RTT
	const auto Texture = FTextureBuild()
	                     .SetSize(1920, 1080)
	                     .SetFormat(GL_DEPTH_COMPONENT)
	                     .SetInternalFormat(GL_DEPTH_COMPONENT)
	                     .SetWrapAround(ETextureTypes::WrapAround::ClampToBorder)
	                     .SetTextureData(GL_FLOAT, nullptr)
	                     .Build();

	RanderTarget = std::make_shared<FRanderTarget>();
	bool Ok = RanderTarget->BindTextureAsDepthAttach(Texture);
	glDrawBuffer(GL_NONE);
	glReadBuffer(GL_NONE);

	DebugerAssert(Ok, "%hs", "Bind Texture Depth 失败");
	RttTexture = RanderTarget->GetTexture(FRanderTarget::DepthTextureName);

	ShadowDepthMaterial = std::make_shared<FDrawShadowDepthMaterial>();
	Ok = ShadowDepthMaterial->Init();
	ShadowDepthMaterial->GetShader()->SetMat4("lightProjectionView", LightPvMat);
	DebugerAssert(Ok, "%hs", "初始化 FDrawShadowDepthMaterial 材质错误");


	// Cube Shadow Light Material
	const auto ShadowMaterial = std::make_shared<FShadowLightMaterial>();
	Ok = ShadowMaterial->InitWithShadowMap(RttTexture);
	CubeMaterial = ShadowMaterial;

	const auto ShadowMaterialShader = CubeMaterial->GetShader();
	ShadowMaterialShader->SetMat4("lightSpaceMatrix", LightPvMat);
	ShadowMaterialShader->SetVec3("lightPos", LightPos);

	DebugerAssert(Ok, "%hs", "初始化 FCubeMaterial 材质错误");

	TestMesh = FDefaultMeshFactory::CreateCube(FDefaultMeshFactory::ECubeVertexType::PosNormalUV);
	TestMesh->OnCheckEnabledSetShaderData = [this](std::map<std::string, bool>& EnabledSetMap)
	{
		if (DrawShadowDepth)
		{
			EnabledSetMap["view"] = false;
			EnabledSetMap["projection"] = false;
			EnabledSetMap["model"] = true;
		}
		else
		{
			EnabledSetMap["view"] = true;
			EnabledSetMap["projection"] = true;
			EnabledSetMap["model"] = true;
		}
	};

	const auto ImgMaterial = std::make_shared<FDrawImgMaterial>(FDrawImgMaterial::ETextureType::Depth);
	ImgMaterial->SetTexture(RttTexture);
	ImgMaterial->Init();

	ImgMesh = FDefaultMeshFactory::CreateQuad();
	ImgMesh->SetMaterial(ImgMaterial);
	ImgMesh->OnCheckEnabledSetShaderData = [](std::map<std::string, bool>& EnabledSetMap)
	{
		EnabledSetMap["view"] = false;
		EnabledSetMap["projection"] = false;
	};

	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

}

void FShadowScence::Destory()
{
	FScence::Destory();

}

void FShadowScence::Draw(const float DateTime)
{
	FScence::Draw(DateTime);


	auto Projection = glm::mat4(1.f);
	Projection = glm::perspective(glm::radians(Camera->GetFOV()), Render->GetAspect(), 0.1f, 100.0f);

	DrawShadowDepth = true;

	RanderTarget->Used();


	FDrawArg LightShadowDrawArg;


	auto ModelMat = glm::mat4(1.f);
	TestMesh->SetTransform(ModelMat);
	TestMesh->SetMaterial(ShadowDepthMaterial);
	TestMesh->Draw(LightShadowDrawArg);

	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
	DrawShadowDepth = false;

	FDrawArg DrawArg;
	DrawArg.DateTime = DateTime;
	DrawArg.LightArray = {};
	DrawArg.ViewMat = Camera->GetViewMat();
	DrawArg.ViewPos = Camera->GetPosition();
	DrawArg.ProjectionMat = Projection;


	auto Mat = glm::mat4(1.0);
	Mat = glm::scale(Mat, glm::vec3(0.4f));
	Mat = glm::translate(Mat, glm::vec3(1.2f, 1.2f, 0.f));
	ImgMesh->SetTransform(Mat);

	auto DrawArgCopy = DrawArg;
	DrawArgCopy.ViewMat = glm::mat4(1.0f);
	ImgMesh->Draw(DrawArgCopy);

	ModelMat = glm::mat4(1.f);
	TestMesh->SetTransform(ModelMat);
	TestMesh->SetMaterial(CubeMaterial);
	TestMesh->Draw(DrawArg);

	ModelMat = glm::mat4(1.f);
	ModelMat = glm::translate(ModelMat, glm::vec3(0, -0.6, 0));
	ModelMat = glm::scale(ModelMat, glm::vec3(20, 0.2, 20));
	TestMesh->SetTransform(ModelMat);
	TestMesh->Draw(DrawArg);
}
